package com.iwdnb.gkgz.application.strategy;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import com.alibaba.fastjson.JSONObject;

import cn.hutool.core.date.DateUtil;
import com.iwdnb.gkgz.application.model.request.AddStrategyTradeStockDataRequest;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StrategyTradeDTO;
import com.iwdnb.gkgz.common.quota.BollingerBands;
import com.iwdnb.gkgz.common.quota.BollingerBands.BollingerData;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import com.iwdnb.gkgz.common.quota.RSIIndicator;
import com.iwdnb.gkgz.common.utils.BigDecimalUtils;
import com.iwdnb.gkgz.common.utils.DateUtils;
import com.iwdnb.gkgz.common.utils.StockUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

/**
 * rsi底背离叠加bolling低价策略
 */
@Service
@Slf4j
public class RsiDeviationStockBackValidateService extends CommmonStockBackValidateService {

    private static final String STATEGY_CODE = "rsiDeviation";
    private BigDecimal thirty = new BigDecimal(30);
    private BigDecimal fourty = new BigDecimal(40);
    private BigDecimal fifty = new BigDecimal(50);

    @Override
    public List<StrategyTradeDTO> queryBackValidateDateList(String code, String strategy,
        List<StockDayData> stockDayDataList, AddStrategyTradeStockDataRequest request) {
        MovingAverage.calculateEMA(stockDayDataList, fivePeriod);
        MovingAverage.calculateEMA(stockDayDataList, twentyPeriod);
        BollingerBands.calculateBollingerBands(stockDayDataList);
        RSIIndicator.calculateRSI(stockDayDataList);
        List<String> dateList = new ArrayList<>();
        List<StrategyTradeDTO> strategyTradeDTOList = new ArrayList<>();
        BigDecimal temp = null;
        BigDecimal lowPrice = null;
        for (int i = 0; i < stockDayDataList.size() - 1; i++) {
            if (i < 60) {
                continue;
            }
            StockDayData data = stockDayDataList.get(i);
            String date = data.getDate();
            BollingerData bollingerData = data.getBollingerBand();
            if (BigDecimalUtils.isGt(data.getClosePrice(), bollingerData.getLowBand())) {
                continue;
            }
            //rsi低值点1
            BigDecimal rsi = StockUtils.getStockQuota(data, RSIIndicator.QUOTA_NAME);
            if (BigDecimalUtils.isGt(rsi, thirty)) {
                continue;
            }
            lowPrice = data.getClosePrice();
            //20日以后出现第二次低点
            List<StockDayData> afterDayDatas = StockUtils.getStockDayDataListAfterDate(stockDayDataList,
                date, 50);
            if (CollectionUtils.isEmpty(afterDayDatas) || afterDayDatas.size() < 20) {
                break;
            }
            //找到rsi低值点1的最低值
            for (int j = 0; j < 10; j++) {
                StockDayData tempData = afterDayDatas.get(j);
                temp = StockUtils.getStockQuota(tempData, RSIIndicator.QUOTA_NAME);
                if (BigDecimalUtils.isLt(temp, rsi)) {
                    rsi = temp;
                    lowPrice = tempData.getClosePrice();
                }
            }
            StockDayData deviationData = null;
            //找到rsi低值点2的最低值
            for (int j = 20; j < afterDayDatas.size(); j++) {
                StockDayData tempData = afterDayDatas.get(j);
                temp = StockUtils.getStockQuota(tempData, RSIIndicator.QUOTA_NAME);
                //找到rsi背离的k线，即价格更低但是rsi值比前值高
                if (BigDecimalUtils.isLt(tempData.getClosePrice(), lowPrice)
                    && BigDecimalUtils.isGt(temp, rsi)) {
                    deviationData = tempData;
                    break;
                }
            }
            if (Objects.isNull(deviationData)) {
                continue;
            }
            date = deviationData.getDate();
            StockDayData buyDayData = null;
            afterDayDatas = StockUtils.getStockDayDataListAfterDate(stockDayDataList,
                date, 10);
            if (CollectionUtils.isEmpty(afterDayDatas)) {
                break;
            }
            for (int j = 3; j < afterDayDatas.size(); j++) {
                StockDayData afterOneData = afterDayDatas.get(j - 2);
                StockDayData afterTwoData = afterDayDatas.get(j - 1);
                StockDayData afterThreeData = afterDayDatas.get(j);
                BigDecimal rsiOne = StockUtils.getStockQuota(afterOneData, RSIIndicator.QUOTA_NAME);
                BigDecimal rsiTwo = StockUtils.getStockQuota(afterTwoData, RSIIndicator.QUOTA_NAME);
                BigDecimal rsiThree = StockUtils.getStockQuota(afterThreeData, RSIIndicator.QUOTA_NAME);
                if (BigDecimalUtils.isGe(rsiTwo, rsiOne)
                    && BigDecimalUtils.isGe(rsiThree, rsiTwo)) {
                    buyDayData = afterThreeData;
                    break;
                }
            }
            if (Objects.isNull(buyDayData)) {
                continue;
            }
            String buyDate = buyDayData.getDate();
            if (!dateList.contains(buyDate)) {
                dateList.add(buyDate);
                StrategyTradeDTO strategyTradeDTO = new StrategyTradeDTO();
                strategyTradeDTO.setCode(code);
                strategyTradeDTO.setUuid(STATEGY_CODE);
                strategyTradeDTO.setBuyDate(DateUtil.parseDate(buyDate));
                strategyTradeDTO.setBuyPrice(data.getClosePrice());
                strategyTradeDTO.setBuyMinPrice(data.getMinPrice());
                strategyTradeDTO.setStatus("buy");
                BigDecimal limitSellPrice = data.getMinPrice();
                strategyTradeDTO.setLimitSellPrice(limitSellPrice);
                strategyTradeDTO.setSignalPrice(data.getClosePrice());
                strategyTradeDTO.setBuySignalRate(
                    BigDecimalUtils.subStractAndDividePrecent(strategyTradeDTO.getSignalPrice(),
                        strategyTradeDTO.getBuyPrice()));
                strategyTradeDTO.setBuySignalMinRate(
                    BigDecimalUtils.subStractAndDividePrecent(strategyTradeDTO.getSignalPrice(),
                        strategyTradeDTO.getBuyMinPrice()));
                JSONObject summary = new JSONObject();
                summary.put("rsi", rsi);
                summary.put("bollingLowBand", bollingerData.getLowBand());
                strategyTradeDTO.setSummary(summary.toJSONString());
                strategyTradeDTOList.add(strategyTradeDTO);
                //BigDecimal profit = caculateAfterDaysProfit(backDayData.getDate(), stockDayDataList, 5);
                //log.debug("{}-{}买入,未来5天最高收益为:{}", code, buyDate, profit);
            }

        }
        return strategyTradeDTOList;
    }

    @Override
    public void doBackValidate(String code, String strategy, List<StockDayData> stockDayDataList,
        List<StrategyTradeDTO> strategyTradeDTOList) {
        List<String> dateList = strategyTradeDTOList.stream().map(o -> DateUtil.formatDate(o.getBuyDate()))
            .collect(Collectors.toList());
        Date latestEndDate = strategyTradeDTOList.get(0).getBuyDate();
        latestEndDate = DateUtil.offsetDay(latestEndDate, -1);
        for (StrategyTradeDTO strategyTradeDTO : strategyTradeDTOList) {
            Date d = strategyTradeDTO.getBuyDate();
            if (DateUtils.beforeOrEquals(d, latestEndDate)) {
                log.debug("{}-{}在上一个周期内,忽略", code, d);
                continue;
            }
            getTradeInfo(code, stockDayDataList, strategyTradeDTO);
            latestEndDate = strategyTradeDTO.getSellDate();
        }
    }

    private void getTradeInfo(String code, List<StockDayData> stockDayDataList,
        StrategyTradeDTO strategyTradeDTO) {
        String date = DateUtil.formatDate(strategyTradeDTO.getBuyDate());
        //log.debug("{}-{}>>>>>股票信息:{}", code, date, strategyTradeDTO.getSummary());
        StockDayData buyDayData = StockUtils.getStockDayData(stockDayDataList, date);
        BigDecimal buyPrice = buyDayData.getClosePrice();
        List<StockDayData> subList = StockUtils.getStockDayDataListAfterDate(stockDayDataList, date, 100);
        if (CollectionUtils.isEmpty(subList) || subList.size() < 10) {
            return;
        }
        int size = subList.size();
        StockDayData firtData = subList.get(0);
        String firstDate = firtData.getDate();
        BigDecimal closeRate = null;
        String sellDate = null;
        String info = "";
        BigDecimal sellPrice = null;
        BigDecimal rate = closeRate;
        int holdIndex = 0;
        BigDecimal rangeMaxPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeMaxRate = BigDecimal.ZERO;
        BigDecimal rangeCloseMaxPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeCloseMaxRate = BigDecimal.ZERO;
        BigDecimal rangeMinPrice = strategyTradeDTO.getBuyPrice();
        BigDecimal rangeMinRate = BigDecimal.ZERO;
        BigDecimal minBollingLow = firtData.getBollingerBand().getLowBand();
        String minBollingDate = firtData.getDate();
        BigDecimal minRsi = StockUtils.getStockQuota(firtData, RSIIndicator.QUOTA_NAME);
        String minRsiDate = firtData.getDate();
        //跌破13日均线累计次数
        int falldownCount = 0;
        BigDecimal temp = null;
        for (int i = 0; i < 100; i++) {
            if (i == size) {
                return;
            }
            StockDayData data = subList.get(i);
            StockDayData yesterdayDayData = i == 0 ? data : subList.get(i - 1);
            BigDecimal closePrice = data.getClosePrice();
            closeRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, buyPrice);
            sellDate = data.getDate();
            sellPrice = data.getClosePrice();
            holdIndex = i;
            BigDecimal upRate = BigDecimalUtils.subStractAndDividePrecent(data.getMaxPrice(), buyPrice);
            BigDecimal openRange = data.getOpenPrice().subtract(buyPrice);
            BigDecimal openRate = BigDecimalUtils.divideToPrecent(openRange, buyPrice);
            rangeMaxPrice = BigDecimalUtils.isGt(data.getMaxPrice(), rangeMaxPrice) ? data.getMaxPrice()
                : rangeMaxPrice;
            rangeMaxRate = BigDecimalUtils.subStractAndDividePrecent(rangeMaxPrice, buyPrice);
            rangeCloseMaxPrice = BigDecimalUtils.isGt(data.getClosePrice(), rangeCloseMaxPrice) ? data.getClosePrice()
                : rangeCloseMaxPrice;
            rangeCloseMaxRate = BigDecimalUtils.subStractAndDividePrecent(rangeCloseMaxPrice, buyPrice);
            rangeMinPrice = BigDecimalUtils.isLt(data.getMinPrice(), rangeMinPrice) ? data.getMinPrice()
                : rangeMinPrice;
            rangeMinRate = BigDecimalUtils.subStractAndDividePrecent(rangeMinPrice, buyPrice);

            if (BigDecimalUtils.isLt(data.getBollingerBand().getLowBand(), minBollingLow)) {
                minBollingLow = data.getBollingerBand().getLowBand();
                minBollingDate = data.getDate();
            }
            temp = StockUtils.getStockQuota(data, RSIIndicator.QUOTA_NAME);
            if (BigDecimalUtils.isLt(temp, minRsi)) {
                minRsi = temp;
                minRsiDate = data.getDate();
            }
            BigDecimal realtimeRate = BigDecimalUtils.subStractAndDividePrecent(data.getClosePrice(), buyPrice);
            rate = closeRate;
            String sellInfo = "收盘价卖出";

            // 跌5个点，跑路
            if (BigDecimalUtils.isLt(closeRate, new BigDecimal(-10))) {
                sellInfo = "跌了超过10个点,止损";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            if (BigDecimalUtils.isGe(realtimeRate, new BigDecimal(50))) {
                Integer yesterdayTradeNum = yesterdayDayData.getTradeNum() * 10;
                if (data.getTradeNum() >= yesterdayTradeNum) {
                    sellInfo = "出现十倍信号量,止盈";
                    log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo,
                        rate,
                        openRate,
                        closeRate, upRate);
                    info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                        + ",最高点收益=" + upRate;
                    break;
                }
            }
            //50个点以上，回落超过2成
            if (BigDecimalUtils.isGe(rangeCloseMaxRate, new BigDecimal(50)) && BigDecimalUtils.isGe(
                rangeCloseMaxRate.multiply(new BigDecimal(0.8)), realtimeRate)) {
                sellInfo = "回落2成,止盈";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }
            //10个点以上，回落到一半
            if (BigDecimalUtils.isGe(rangeMaxRate, new BigDecimal(10))
                && BigDecimalUtils.isLe(realtimeRate.multiply(new BigDecimal(2)), rangeMaxRate)) {
                sellInfo = "10个点以上回落到一般左右,止盈";
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate, closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }

            if (i == 99) {
                log.debug("第{}天-{}-{}-{}，收益={}，开盘收益={},收盘收益={},最高点收益={}", (i + 1), code, data.getDate(), sellInfo, rate,
                    openRate,
                    closeRate, upRate);
                info += "第" + (i + 1) + "天" + sellInfo + ",收益=" + rate + ",开盘收益=" + openRate + ",收盘收益=" + closeRate
                    + ",最高点收益=" + upRate;
                break;
            }

        }
        strategyTradeDTO.setHoldDay(holdIndex + 1);
        strategyTradeDTO.setSellDate(DateUtil.parseDate(sellDate));
        strategyTradeDTO.setSellPrice(sellPrice);
        strategyTradeDTO.setProfitRate(rate);
        strategyTradeDTO.setRangeMaxPrice(rangeMaxPrice);
        strategyTradeDTO.setRangeMaxRate(rangeMaxRate);
        strategyTradeDTO.setRangeMinPrice(rangeMinPrice);
        strategyTradeDTO.setRangeMinRate(rangeMinRate);
        strategyTradeDTO.setStrategyDescription(info);
        strategyTradeDTO.setStatus("sell");
        strategyTradeDTO.setWinFlag(BigDecimalUtils.isGt(rate, BigDecimal.ZERO) ? "y" : "n");
        JSONObject o = new JSONObject();
        o.put("minBollingLow", minBollingLow);
        o.put("minBollingDate", minBollingDate);
        o.put("hasMinBolling", !StringUtils.equals(minBollingDate, firstDate));
        o.put("minRsi", minRsi);
        o.put("minRsiDate", minRsiDate);
        o.put("hasMinRsi", !StringUtils.equals(minRsiDate, firstDate));
        strategyTradeDTO.setFollowInfo(o.toJSONString());
    }

}
